home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / perl5 / HTML / Formatter.pm < prev    next >
Encoding:
Perl POD Document  |  2004-06-02  |  20.7 KB  |  987 lines

  1.  
  2. require 5;
  3. package HTML::Formatter;
  4.  
  5. =head1 NAME
  6.  
  7. HTML::Formatter - Base class for HTML formatters
  8.  
  9. =head1 SYNOPSIS
  10.  
  11.   use HTML::FormatSomething;
  12.   my $infile  = "whatever.html";
  13.   my $outfile = "whatever.file";
  14.   open OUT, ">$outfile"
  15.    or die "Can't write-open $outfile: $!\nAborting";
  16.   binmode(OUT);
  17.   print OUT HTML::FormatSomething->format_file(
  18.     $infile,
  19.       'option1' => 'value1',
  20.       'option2' => 'value2',
  21.       ...
  22.   );
  23.   close(OUT);
  24.  
  25. =head1 DESCRIPTION
  26.  
  27. HTML::Formatter is a base class for classes that take HTML
  28. and format it to some output format.  When you take an object
  29. of such a base class and call C<< $formatter->format( $tree ) >>
  30. with an HTML::TreeBuilder (or HTML::Element) object, they return
  31. the 
  32.  
  33. HTML formatters are able to format a HTML syntax tree into various
  34. printable formats.  Different formatters produce output for different
  35. output media.  Common for all formatters are that they will return the
  36. formatted output when the format() method is called.  The format()
  37. method takes a HTML::Element object (usually the HTML::TreeBuilder
  38. root object) as parameter.
  39.  
  40. Here are the four main methods that this class provides:
  41.  
  42. =over
  43.  
  44. =item SomeClass->format_file( $filename, I<< option1 => value1, option2 => value2, ... >> )
  45.  
  46. This returns a string consisting of the result of using the given class
  47. to format the given HTML file according to the given (optional) options.
  48. Internally it calls C<< SomeClass->new( ... )->format( ... ) >> on a new
  49. HTML::TreeBuilder object based on the given HTML file.
  50.  
  51. =item SomeClass->format_string( $html_source, I<< option1 => value1, option2 => value2, ... >> )
  52.  
  53. This returns a string consisting of the result of using the given class
  54. to format the given HTML source according to the given (optional)
  55. options. Internally it calls C<< SomeClass->new( ... )->format( ... ) >>
  56. on a new HTML::TreeBuilder object based on the given source.
  57.  
  58. =item $formatter = SomeClass->new( I<< option1 => value1, option2 => value2, ... >> )
  59.  
  60. This creates a new formatter object with the given options.
  61.  
  62. =item $render_string = $formatter->format( $html_tree_object )
  63.  
  64. This renders the given HTML object accerting to the options set for
  65. $formatter.
  66.  
  67. =back
  68.  
  69.  
  70. After you've used a particular formatter object to format a particular
  71. HTML tree object, you probably should not use either again.
  72.  
  73.  
  74. =head1 SEE ALSO
  75.  
  76. L<HTML::FormatText>, L<HTML::FormatPS>,
  77. L<HTML::FormatRTF>
  78.  
  79. L<HTML::TreeBuilder>, L<HTML::Element>, L<HTML::Tree>
  80.  
  81.  
  82.  
  83. =head1 COPYRIGHT
  84.  
  85. Copyright (c) 1995-2002 Gisle Aas, and 2002- Sean M. Burke. All rights
  86. reserved.
  87.  
  88. This library is free software; you can redistribute it and/or
  89. modify it under the same terms as Perl itself.
  90.  
  91. This program is distributed in the hope that it will be useful, but
  92. without any warranty; without even the implied warranty of
  93. merchantability or fitness for a particular purpose.
  94.  
  95.  
  96. =head1 AUTHOR
  97.  
  98. Current maintainer: Sean M. Burke <sburke@cpan.org>
  99.  
  100. Original author: Gisle Aas <gisle@aas.no>
  101.  
  102. =cut
  103.  
  104. BEGIN { *DEBUG = sub(){0} unless defined &DEBUG }
  105.  
  106. use HTML::Element 3.15 ();
  107.  
  108. use strict;
  109. use Carp;
  110. use UNIVERSAL qw(can);
  111.  
  112. use vars qw($VERSION @Size_magic_numbers);
  113. $VERSION = sprintf("%d.%02d", q$Revision: 2.04 $ =~ /(\d+)\.(\d+)/);
  114.  
  115. #
  116. # A typical formatter will not use all of the features of this
  117. # class.  But it will use some, as best fits the mapping
  118. # of HTML to the particular output format.
  119. #
  120.  
  121. sub new
  122. {
  123.     my($class,%arg) = @_;
  124.     my $self = bless { $class->default_values }, $class;
  125.     $self->configure(\%arg) if keys %arg;
  126.     $self;
  127. }
  128.  
  129. sub default_values
  130. {
  131.     ();
  132. }
  133.  
  134. sub configure
  135. {
  136.     my($self, $arg) = @_;
  137.     for (keys %$arg) {
  138.     warn "Unknown configure argument '$_'" if $^W;
  139.     }
  140.     $self;
  141. }
  142.  
  143. sub massage_tree {
  144.   my($self, $html) = @_;
  145.   return if $html->tag eq 'p'; # sanity
  146.  
  147.   DEBUG > 4 and print("Before massaging:\n"), $html->dump();
  148.  
  149.   $html->simplify_pres();
  150.   
  151.   # Does anything else need doing?
  152.  
  153.   DEBUG > 4 and print("After massaging:\n"), $html->dump();
  154.  
  155.   return;
  156. }
  157.  
  158. # forgiving aliases
  159. sub format_from_file   { shift->format_file(@_) }
  160. sub format_from_string { shift->format_string(@_) }
  161.  
  162. sub format_file {
  163.   my($self, $filename, @params) = @_;
  164.   $self = $self->new(@params) unless ref $self;
  165.  
  166.   croak "What filename to format from?"
  167.    unless defined $filename and length $filename;
  168.  
  169.   my $tree = $self->_default_tree();
  170.   $tree->parse_file($filename);
  171.   
  172.   my $out = $self->format($tree);
  173.   $tree->delete;
  174.   return $out;
  175. }
  176.  
  177. sub format_string {
  178.   my($self, $content, @params) = @_;
  179.   $self = $self->new(@params) unless ref $self;
  180.  
  181.   croak "What string to format?" unless defined $content;
  182.  
  183.   my $tree = $self->_default_tree();
  184.   $tree->parse($content);
  185.   $tree->eof();
  186.   undef $content;
  187.  
  188.   my $out = $self->format($tree);
  189.   $tree->delete;
  190.   return $out;
  191. }
  192.  
  193. sub _default_tree {
  194.   require HTML::TreeBuilder;
  195.   my $t = HTML::TreeBuilder->new;
  196.   
  197.   # If nothing else works, try using these parser options:s
  198.   #$t->implicit_body_p_tag(1);
  199.   #$t->p_strict(1);
  200.   
  201.   return $t;
  202. }
  203.  
  204.  
  205. sub format
  206. {
  207.     my($self, $html) = @_;
  208.  
  209.     croak "Usage: \$formatter->format(\$tree)"
  210.      unless defined $html and ref $html and can($html, 'tag');
  211.  
  212.     if( $self->DEBUG() > 4 ) {
  213.       print "Tree to format:\n";
  214.       $html->dump;
  215.     }
  216.  
  217.     $self->set_version_tag($html);
  218.     $self->massage_tree($html);
  219.     $self->begin($html);
  220.     $html->number_lists();
  221.     
  222.  
  223.     # Per-iteration scratch:
  224.     my($node, $start, $depth, $tag, $func);
  225.     $html->traverse(
  226.     sub {
  227.         ($node, $start, $depth) = @_;
  228.         if (ref $node) {
  229.         $tag = $node->tag;
  230.         $func = $tag . '_' . ($start ? "start" : "end");
  231.         # Use UNIVERSAL::can so that we can recover if
  232.         # a handler is not defined for the tag.
  233.         if (can($self, $func)) {
  234.             DEBUG > 3 and print '  ' x $depth, "Calling $func\n";
  235.             return $self->$func($node);
  236.         } else {
  237.             DEBUG > 3 and print '  ' x $depth,
  238.               "Skipping $func: no handler for it.\n";
  239.             return 1;
  240.         }
  241.         } else {
  242.         $self->textflow($node);
  243.         }
  244.         1;
  245.     }
  246.     );
  247.     $self->end($html);
  248.     join('', @{$self->{output}});
  249. }
  250.  
  251. sub begin
  252. {
  253.     my $self = shift;
  254.  
  255.     # Flags
  256.     $self->{anchor}    = 0;
  257.     $self->{underline} = 0;
  258.     $self->{bold}      = 0;
  259.     $self->{italic}    = 0;
  260.     $self->{center}    = 0;
  261.  
  262.     $self->{superscript}   = 0;
  263.     $self->{subscript}     = 0;
  264.     $self->{strikethrough} = 0;
  265.  
  266.     $self->{center_stack} = []; # push and pop 'center' states to it
  267.     $self->{nobr}      = 0;
  268.  
  269.     $self->{'font_size'}   = [3];   # last element is current size
  270.     $self->{basefont_size} = [3];
  271.  
  272.     $self->{vspace} = undef;        # vertical space (dimension)
  273.  
  274.     $self->{output} = [];
  275. }
  276.  
  277. sub end
  278. {
  279. }
  280.  
  281. #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  282. sub set_version_tag {
  283.   my($self, $html) = @_;
  284.   
  285.   if($html) {
  286.     $self->{'version_tag'} = sprintf(
  287.       "%s (v%s, using %s v%s%s)",
  288.       ref($self), $self->VERSION || '?',
  289.       ref($html), $html->VERSION || '?',
  290.       $HTML::Parser::VERSION
  291.         ? ", and HTML::Parser v$HTML::Parser::VERSION"
  292.         : ''
  293.     );
  294.   } elsif( $HTML::Parser::VERSION ) {
  295.     $self->{'version_tag'} = sprintf(
  296.       "%s (v%s, using %s)",
  297.       ref($self), $self->VERSION || "?",
  298.       "HTML::Parser v$HTML::Parser::VERSION",
  299.     );
  300.   } else {
  301.     $self->{'version_tag'} = sprintf(
  302.       "%s (v%s)",
  303.       ref($self), $self->VERSION || '?',
  304.     );
  305.   }
  306. }
  307.  
  308. #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  309.  
  310. sub version_tag { shift->{'version_tag'} }
  311.  
  312. #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  313.  
  314. sub html_start { 1; }  sub html_end {}
  315. sub body_start { 1; }  sub body_end {}
  316.  
  317. # some elements that we don't want to render anyway
  318. sub     head_start { 0; }
  319. sub   script_start { 0; }
  320. sub    style_start { 0; }
  321. sub frameset_start { 0; }
  322.  
  323.  
  324. sub header_start
  325. {
  326.     my($self, $level, $node) = @_;
  327.     my $align = $node->attr('align');
  328.     if (defined($align) && lc($align) eq 'center') {
  329.     $self->{center}++;
  330.     }
  331.     1;
  332. }
  333.  
  334. sub header_end
  335. {
  336.     my($self, $level, $node) = @_;
  337.     my $align = $node->attr('align');
  338.     if (defined($align) && lc($align) eq 'center') {
  339.     $self->{center}--;
  340.     }
  341. }
  342.  
  343. sub h1_start { shift->header_start(1, @_) }
  344. sub h2_start { shift->header_start(2, @_) }
  345. sub h3_start { shift->header_start(3, @_) }
  346. sub h4_start { shift->header_start(4, @_) }
  347. sub h5_start { shift->header_start(5, @_) }
  348. sub h6_start { shift->header_start(6, @_) }
  349.  
  350. sub h1_end   { shift->header_end(1, @_) }
  351. sub h2_end   { shift->header_end(2, @_) }
  352. sub h3_end   { shift->header_end(3, @_) }
  353. sub h4_end   { shift->header_end(4, @_) }
  354. sub h5_end   { shift->header_end(5, @_) }
  355. sub h6_end   { shift->header_end(6, @_) }
  356.  
  357. sub br_start
  358. {
  359.     my $self = shift;
  360.     $self->vspace(0, 1);
  361.      # add one formatting newline, regardless of how many are there
  362. }
  363.  
  364. sub hr_start
  365. {
  366.     my $self = shift;
  367.     $self->vspace(1);
  368.      # assert one line's worth of vertical space
  369.     1;
  370. }
  371.  
  372. sub img_start
  373. {
  374.     my($self,$node) = @_;
  375.     my $alt = $node->attr('alt');
  376.     $self->out(  defined($alt) ? $alt : "[IMAGE]" );
  377. }
  378.  
  379. sub a_start
  380. {
  381.     shift->{anchor}++;
  382.     1;
  383. }
  384.  
  385. sub a_end
  386. {
  387.     shift->{anchor}--;
  388. }
  389.  
  390.  
  391. sub u_start
  392. {
  393.     shift->{underline}++;
  394.     1;
  395. }
  396.  
  397. sub u_end
  398. {
  399.     shift->{underline}--;
  400. }
  401.  
  402. sub b_start
  403. {
  404.     shift->{bold}++;
  405.     1;
  406. }
  407.  
  408. sub b_end
  409. {
  410.     shift->{bold}--;
  411. }
  412.  
  413. sub tt_start
  414. {
  415.     shift->{teletype}++;
  416.     1;
  417. }
  418.  
  419. sub tt_end
  420. {
  421.     shift->{teletype}--;
  422. }
  423.  
  424. sub i_start
  425. {
  426.     shift->{italic}++;
  427.     1;
  428. }
  429.  
  430. sub i_end
  431. {
  432.     shift->{italic}--;
  433. }
  434.  
  435. sub center_start
  436. {
  437.     shift->{center}++;
  438.     1;
  439. }
  440.  
  441. sub center_end
  442. {
  443.     shift->{center}--;
  444. }
  445.  
  446.  
  447. sub div_start   # interesting only for its 'align' attribute
  448. {
  449.     my($self, $node) = @_;
  450.     my $align = $node->attr('align');
  451.     if (defined($align) && lc($align) eq 'center') {
  452.     return $self->center_start;
  453.     }
  454.     1;
  455. }
  456.  
  457. sub div_end
  458. {
  459.     my($self, $node) = @_;
  460.     my $align = $node->attr('align');
  461.     if (defined($align) && lc($align) eq 'center') {
  462.     return $self->center_end;
  463.     }
  464. }
  465.  
  466.  
  467. sub nobr_start
  468. {
  469.     shift->{nobr}++;
  470.     1;
  471. }
  472.  
  473. sub nobr_end
  474. {
  475.     shift->{nobr}--;
  476. }
  477.  
  478. sub wbr_start
  479. {
  480.     1;
  481. }
  482.  
  483. # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
  484.  
  485. sub font_start
  486. {
  487.     my($self, $elem) = @_;
  488.     my $size = $elem->attr('size');
  489.     return 1 unless defined $size;
  490.     if ($size =~ /^\s*[+\-]/) {
  491.     my $base = $self->{basefont_size}[-1];
  492.       # yes, base it on the most recent one
  493.     $size = $base + $size;
  494.     }
  495.     push @{$self->{'font_size'}}, $size;
  496.     $self->new_font_size( $size );
  497.     1;
  498. }
  499.  
  500. sub font_end
  501. {
  502.     my($self, $elem) = @_;
  503.     my $size = $elem->attr('size');
  504.     return unless defined $size;
  505.     pop @{$self->{'font_size'}};
  506.     $self->restore_font_size(  $self->{'font_size'}[-1]  );
  507. }
  508.  
  509.  
  510.  
  511. sub big_start
  512. {
  513.     my $self = $_[0];
  514.     push @{$self->{'font_size'}},
  515.       $self->{basefont_size}[-1] + 1;   # same as font size="+1"
  516.     $self->new_font_size(  $self->{'font_size'}[ -1 ]  );
  517.     1;
  518. }
  519.  
  520. sub small_start
  521. {
  522.     my $self = $_[0];
  523.     push @{$self->{'font_size'}},
  524.       $self->{basefont_size}[-1] - 1,   # same as font size="-1"
  525.     ;
  526.     $self->new_font_size(  $self->{'font_size'}[ -1 ]  );
  527.     1;
  528. }
  529.  
  530. sub big_end 
  531. {
  532.     my $self = $_[0];
  533.     pop @{ $self->{'font_size'} };
  534.     $self->restore_font_size(  $self->{'font_size'}[-1]  );
  535.     1;
  536. }
  537.  
  538. sub small_end 
  539. {
  540.     my $self = $_[0];
  541.     pop @{ $self->{'font_size'} };
  542.     $self->restore_font_size(  $self->{'font_size'}[-1]  );
  543.     1;
  544. }
  545.  
  546. # ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
  547.  
  548. sub basefont_start
  549. {
  550.     my($self, $elem) = @_;
  551.     my $size = $elem->attr('size');
  552.     return unless defined $size;
  553.     push(@{$self->{basefont_size}}, $size);
  554.     1;
  555. }
  556.  
  557. sub basefont_end
  558. {
  559.     my($self, $elem) = @_;
  560.     my $size = $elem->attr('size');
  561.     return unless defined $size;
  562.     pop(@{$self->{basefont_size}});
  563. }
  564.  
  565. #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  566. # Override in subclasses, if you like.
  567.  
  568. sub new_font_size {
  569.     #my( $self, $font_size_number ) = @_;
  570. }
  571.  
  572. sub restore_font_size {
  573.     #my( $self, $font_size_number ) = @_;
  574. }
  575.  
  576. #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  577.  
  578. sub q_start { shift->out( q<"> ); 1; }
  579. sub q_end   { shift->out( q<"> ); 1; }
  580.  
  581.  
  582. sub sup_start { shift->{superscript}++; 1; }
  583. sub sup_end   { shift->{superscript}--; 1; }
  584.  
  585. sub sub_start { shift->{subscript}  ++; 1; }
  586. sub sub_end   { shift->{subscript}  --; 1; }
  587.  
  588. sub strike_start { shift->{strikethrough}++; 1; }
  589. sub strike_end   { shift->{strikethrough}--; 1; }
  590.  
  591. # Alias:
  592. sub s_start { shift->strike_start(@_) }
  593. sub s_end   { shift->strike_end(  @_) }
  594.  
  595.  
  596. ## No actual appearance change, so no point in defining:
  597. #
  598. # sub dfn_start { 1; }
  599. # sub dfn_end   { 1; }
  600. # sub abbr_start { 1; }
  601. # sub abbr_end   { 1; }
  602. # sub acronym_start { 1; }
  603. # sub acronym_end   { 1; }
  604. # sub span_start { 1; }
  605. # sub span_end   { 1; }
  606. # sub div_start { 1; }
  607. # sub div_end   { 1; }
  608. # sub ins_start { 1; }
  609. # sub ins_end   { 1; }
  610.  
  611. sub del_start { 0; } # Don't render the del'd bits
  612. sub del_end   { 0; }
  613.  
  614. @Size_magic_numbers = (
  615.   .60,  .75,  .89,   1,  1.20,  1.50,  2.00,  3.00
  616.  # #0    #1    #2   #3     #4     #5     #6     #7
  617.  #________________ - | + _________________________
  618.  # -3    -2    -1    0     +1     +2     +3     +4
  619. );
  620.  
  621. sub scale_font_for {
  622.   my($self, $reference_size) = @_;
  623.   
  624.   # Mozilla's source, at
  625.   # http://lxr.mozilla.org/seamonkey/source/content/html/style/src/nsStyleUtil.cpp#299
  626.   # says:
  627.   #  static PRInt32 sFontSizeFactors[8] = { 60,75,89,100,120,150,200,300 };
  628.   #
  629.   # For comparison, Gisle's earlier HTML::FormatPS has:
  630.   #    |           # size   0   1   2   3   4   5   6   7
  631.   #    | @FontSizes = ( 5,  6,  8, 10, 12, 14, 18, 24, 32);
  632.   # ...and gets different sizing via just a scaling factor.
  633.  
  634.   my $size_number = int( defined($_[2]) ? $_[2] : $self->{'font_size'}[-1] );
  635.  
  636.   # force the size_number into range:
  637.   $size_number =
  638.       ( $size_number < 0 ) ?  0
  639.     : ( $size_number > $#Size_magic_numbers ) ?  $#Size_magic_numbers
  640.     : int( $size_number )
  641.   ;
  642.   
  643.   my $result = int( .5 + $reference_size * $Size_magic_numbers[ $size_number ] );
  644.  
  645.   $self->DEBUG() > 1
  646.    and printf "  Turning reference size %s and size number %s into %s.\n",
  647.     $reference_size, $size_number, $result,
  648.   ;
  649.  
  650.   return $result;
  651. }
  652.  
  653.  
  654. # Aliases for logical markup:
  655. sub strong_start   { shift-> b_start( @_) }
  656. sub strong_end     { shift-> b_end(   @_) }
  657. sub   cite_start   { shift-> i_start( @_) }
  658. sub   cite_end     { shift-> i_end(   @_) }
  659. sub     em_start   { shift-> i_start( @_) }
  660. sub     em_end     { shift-> i_end(   @_) }
  661. sub   code_start   { shift->tt_start( @_) }
  662. sub   code_end     { shift->tt_end(   @_) }
  663. sub    kbd_start   { shift->tt_start( @_) }
  664. sub    kbd_end     { shift->tt_end(   @_) }
  665. sub   samp_start   { shift->tt_start( @_) }
  666. sub   samp_end     { shift->tt_end(   @_) }
  667. sub    var_start   { shift->tt_start( @_) }
  668. sub    var_end     { shift->tt_end(   @_) }
  669.  
  670. sub p_start
  671. {
  672.     my $self = shift;
  673.     #$self->adjust_lm(0); # assert new paragraph
  674.     $self->vspace(1);
  675.      # assert one line's worth of vertical space at para-start
  676.     $self->out('');
  677.     1;
  678. }
  679.  
  680. sub p_end
  681. {
  682.     shift->vspace(1);
  683.      # assert one line's worth of vertical space at para-end
  684. }
  685.  
  686. sub pre_start
  687. {
  688.     my $self = shift;
  689.     $self->{pre}++;
  690.     $self->vspace(1);
  691.      # assert one line's worth of vertical space at pre-start
  692.     1;
  693. }
  694.  
  695. sub pre_end
  696. {
  697.     my $self = shift;
  698.     $self->{pre}--;
  699.      # assert one line's worth of vertical space at pre-end
  700.     $self->vspace(1);
  701. }
  702.  
  703. sub listing_start      { shift->pre_start( @_ ) }
  704. sub listing_end        { shift->pre_end(   @_ ) }
  705. sub     xmp_start      { shift->pre_start( @_ ) }
  706. sub     xmp_end        { shift->pre_end(   @_ ) }
  707.  
  708. sub blockquote_start
  709. {
  710.     my $self = shift;
  711.     $self->vspace(1);
  712.      # assert one line's worth of vertical space at blockquote-start
  713.     $self->adjust_lm( +2 );
  714.     $self->adjust_rm( -2 );
  715.     1;
  716. }
  717.  
  718. sub blockquote_end
  719. {
  720.     my $self = shift;
  721.     $self->vspace(1);
  722.      # assert one line's worth of vertical space at blockquote-end
  723.     $self->adjust_lm( -2 );
  724.     $self->adjust_rm( +2 );
  725. }
  726.  
  727. sub address_start
  728. {
  729.     my $self = shift;
  730.     $self->vspace(1);
  731.      # assert one line's worth of vertical space at address-para-start
  732.     $self->i_start(@_);
  733.     1;
  734. }
  735.  
  736. sub address_end
  737. {
  738.     my $self = shift;
  739.     $self->i_end(@_);
  740.      # assert one line's worth of vertical space at address-para-end
  741.     $self->vspace(1);
  742. }
  743.  
  744. # Handling of list elements
  745.  
  746. sub ul_start
  747. {
  748.     my $self = shift;
  749.     $self->vspace(1);
  750.      # assert one line's worth of vertical space at ul-start
  751.     $self->adjust_lm( +2 );
  752.     1;
  753. }
  754.  
  755. sub ul_end
  756. {
  757.     my $self = shift;
  758.     $self->adjust_lm( -2 );
  759.      # assert one line's worth of vertical space at ul-end
  760.     $self->vspace(1);
  761. }
  762.  
  763. sub li_start
  764. {
  765.     my $self = shift;
  766.     $self->bullet( shift->attr('_bullet') || '' );
  767.     $self->adjust_lm(+2);
  768.     1;
  769. }
  770.  
  771. sub bullet
  772. {
  773.     shift->out(@_);
  774. }
  775.  
  776. sub li_end
  777. {
  778.     my $self = shift;
  779.     $self->vspace(1);
  780.     $self->adjust_lm( -2);
  781. }
  782.  
  783. sub menu_start      { shift->ul_start(@_) }
  784. sub menu_end        { shift->ul_end(@_) }
  785. sub  dir_start      { shift->ul_start(@_) }
  786. sub  dir_end        { shift->ul_end(@_) }
  787.  
  788. sub ol_start
  789. {
  790.     my $self = shift;
  791.  
  792.     $self->vspace(1);
  793.     $self->adjust_lm(+2);
  794.     1;
  795. }
  796.  
  797. sub ol_end
  798. {
  799.     my $self = shift;
  800.     $self->adjust_lm(-2);
  801.     $self->vspace(1);
  802. }
  803.  
  804.  
  805. sub dl_start
  806. {
  807.     my $self = shift;
  808.     # $self->adjust_lm(+2);
  809.     $self->vspace(1);
  810.      # assert one line's worth of vertical space at dl-start
  811.     1;
  812. }
  813.  
  814. sub dl_end
  815. {
  816.     my $self = shift;
  817.     # $self->adjust_lm(-2);
  818.     $self->vspace(1);
  819.      # assert one line's worth of vertical space at dl-end
  820. }
  821.  
  822.  
  823. sub dt_start
  824. {
  825.     my $self = shift;
  826.     $self->vspace(1);
  827.      # assert one line's worth of vertical space at dt-start
  828.     1;
  829. }
  830.  
  831. sub dt_end
  832. {
  833. }
  834.  
  835.  
  836. sub dd_start
  837. {
  838.     my $self = shift;
  839.     $self->adjust_lm(+6);
  840.     $self->vspace(0);
  841.      # hm, what's that do?  nothing?
  842.     1;
  843. }
  844.  
  845. sub dd_end
  846. {
  847.     my $self = shift;
  848.     $self->vspace(1);
  849.      # assert one line's worth of vertical space at dd-end
  850.     $self->adjust_lm(-6);
  851. }
  852.  
  853. #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  854.  
  855. # And now some things that are basically sane fall-throughs for classes
  856. #  that don't really handle tables or forms specially...
  857.  
  858. # Things not formatted at all
  859. sub input_start    { 0; }
  860. sub textarea_start { 0; }
  861. sub select_start   { 0; }
  862. sub option_start   { 0; }
  863.  
  864. sub td_start {
  865.   my $self = shift;
  866.   
  867.   push @{$self->{'center_stack'}}, $self->{'center'};
  868.   $self->{center} = 0;
  869.   
  870.   $self->p_start(@_);
  871. }
  872. sub td_end {
  873.   my $self = shift;
  874.   $self->{'center'} = pop @{$self->{'center_stack'}};
  875.   $self->p_end(@_);
  876. }
  877.  
  878. sub th_start {
  879.   my $self = shift;
  880.  
  881.   push @{$self->{'center_stack'}}, $self->{'center'};
  882.   $self->{center} = 0;
  883.  
  884.   $self->p_start(@_);
  885.   $self->b_start(@_);
  886. }
  887. sub th_end {
  888.   my $self = shift;
  889.   $self->b_end(@_);
  890.   $self->{'center'} = pop @{$self->{'center_stack'}};
  891.   $self->p_end(@_);
  892. }
  893.  
  894. # But if you wanted to just SKIP tables and forms, you'd do this:
  895. #  sub table_start { shift->out('[TABLE NOT SHOWN]'); 0; }
  896. #  sub form_start  { shift->out('[FORM NOT SHOWN]');  0; }
  897.  
  898. #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  899.  
  900. sub textflow
  901. {
  902.     my $self = shift;
  903.     if ($self->{pre}) {
  904.     # Strip one leading and one trailing newline so that a <pre>
  905.     #  tag can be placed on a line of its own without causing extra
  906.     #  vertical space as part of the preformatted text.
  907.     $_[0] =~ s/\n$//;
  908.     $_[0] =~ s/^\n//;
  909.     $self->pre_out( $_[0] );
  910.     } else {
  911.     for (split(/(\s+)/, $_[0])) {
  912.         next unless length $_;
  913.         $self->out($_);
  914.     }
  915.     }
  916. }
  917.  
  918.  
  919.  
  920. sub vspace
  921. {
  922.     # This method sets the vspace attribute.  When vspace is
  923.     # defined, then a new line should be started.  If vspace
  924.     # is a nonzero value, then that should be taken as the
  925.     # number of lines to be skipped before following text
  926.     # is written out.
  927.     #
  928.     # You may think it odd to conflate the two concepts of
  929.     # ending this paragraph, and asserting how much space should
  930.     # follow; but it happens to work out pretty well.
  931.     
  932.     my($self, $min, $add) = @_;
  933.     my $old = $self->{vspace};
  934.     if (defined $old) {
  935.     my $new = $old;
  936.     $new += $add || 0;
  937.     $new = $min if $new < $min;
  938.     $self->{vspace} = $new;
  939.     } else {
  940.     $self->{vspace} = $min;
  941.         DEBUG > 1 and print " vspace not set, so setting to $min\n";
  942.     #my $new = $add || 0;
  943.     #$new = $min if $new < $min;
  944.     #$self->{vspace} = $new;
  945.     }
  946.     DEBUG > 1 and print " vspace now set to $min\n";
  947.     $old;
  948. }
  949.  
  950. sub collect
  951. {
  952.     push(@{shift->{output}}, @_);
  953. }
  954.  
  955. #``````````````````````````````````````````````````````````````````````````
  956.  
  957. sub out  # Output a word
  958. {
  959.     # my($self, $text) = @_;
  960.     # $text =~ tr/\xA0\xAD/ /d;
  961.       # The 0xAD-killing is if you don't support anything like a soft hyphen
  962.       #  in your destination format
  963.  
  964.     confess "Must be overridden by subclass";
  965. }
  966.  
  967. sub pre_out
  968. {
  969.     confess "Must be overridden by subclass";
  970. }
  971.  
  972.  
  973. sub adjust_lm
  974. {
  975.     confess "Must be overridden by subclass";
  976. }
  977.  
  978. sub adjust_rm
  979. {
  980.     confess "Must be overridden by subclass";
  981. }
  982.  
  983.  
  984. #``````````````````````````````````````````````````````````````````````````
  985. 1;
  986.  
  987.